package org.utils.net;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.utils.io.FileUtils;

/**
 * @author eveno
 *
 */
public abstract class NetworkInfo
{
	protected static final String LOCALHOST = "localhost";
	public static final String NSLOOKUP_CMD = "nslookup";

	/**
	 * 
	 */
	public NetworkInfo()
	{
		super();
	}

	/** Universal entry for retrieving MAC Address */
	public abstract String getMacAddress() throws IOException;

	/** Universal entry for retrieving domain info */
	public final String getNetworkDomain() throws IOException
	{
		return getNetworkDomain(NetworkUtils.getHostname());
	}

	/** Universal entry for retrieving domain info */
	public final String getNetworkDomain(String pHostname) throws IOException
	{
		// get the address of the host we are looking for - verification
		java.net.InetAddress addy = java.net.InetAddress.getByName(pHostname);
		// back out to the hostname - just validating
		String hostname = addy.getHostName();
		String nslookupCommand = NSLOOKUP_CMD + " " + hostname;

		// run the lookup command
		String nslookupResponse = runConsoleCommand(nslookupCommand);

		StringTokenizer tokeit = new StringTokenizer(nslookupResponse, "\n", false);
		while (tokeit.hasMoreTokens())
		{
			String line = tokeit.nextToken();
			if (line.startsWith("Name:") || line.startsWith("Server:")
					|| line.startsWith("Nom :") || line.startsWith("Serveur :"))
			{
				line = line.substring(line.indexOf(":") + 1);
				line = line.trim();
				if (isDomain(line, hostname))
				{
					line = line.substring(hostname.length() + 1);
					return line;
				}
			}
		}

		return "Not Available";
	}

	private static boolean isDomain(String domainCandidate, String hostname)
	{
//		Pattern domainPattern = Pattern.compile("[\\w-]+\\.[\\w-]+\\.[\\w-]+\\.[\\w-]+");
//		Matcher m = domainPattern.matcher(domainCandidate);
//		return m.matches() && domainCandidate.startsWith(hostname);
		return domainCandidate.startsWith(hostname);
	}

	protected String runConsoleCommand(String command) throws IOException
	{
		Process p = Runtime.getRuntime().exec(command);
		InputStream stdoutStream = new BufferedInputStream(p.getInputStream());

		StringBuffer buffer = new StringBuffer();
		for (;;)
		{
			int c = stdoutStream.read();
			if (c == -1)
				break;
			buffer.append((char) c);
		}
		String outputText = buffer.toString();

		stdoutStream.close();

		return outputText;
	}

    /**
     * Tested to work with results from ARP command under Solaris, Linux, MS-Windows
     * Note it does not always return a Mac address, even if the host exists. This represents more of 
     * a best 'effort' solution.
     */
    protected String getMacAddress ( String host ) throws UnknownHostException, IOException, InterruptedException
    {
        String macAddress = null;
        InetAddress hostAddress = InetAddress.getByName( host );
        
        String resultLine = callArpTool ( hostAddress.getHostAddress() );
        
        if ( resultLine == null )
        {
            return null;
        }
        if ( resultLine.indexOf("\n") > -1 )
        {
            String[] lines = resultLine.split("\n");
            resultLine = lines[lines.length -1];
        }
        if ( resultLine.indexOf("--") > -1 || resultLine.indexOf("unknown host") > -1 )
        {
            return null;
        }
                
        int idx = -1;
        if ( resultLine.indexOf( '-' ) > -1 )
        {
            idx = resultLine.indexOf( '-' ) - 2;
        }
        else if ( resultLine.indexOf( ':' ) > -1 )
        {
            idx = resultLine.indexOf( ':' ) - 2;
        }        
          
        int endIdx = resultLine.length()-1;
        if ( resultLine.indexOf(' ',idx+1) > -1 )
        {
            endIdx = resultLine.indexOf(' ',idx+1);
        }
        macAddress = resultLine.substring(idx,endIdx);
        
        return macAddress;
    }
    
    private String callArpTool ( String ipAddress ) throws IOException, InterruptedException
    {
        /* samples:
        //solaris
        result = "motako (10.1.12.79) at 0:e:a6:b5:70:80";
        result = "arp: motakox: unknown host";
        //linux
        result = "Address                  HWtype  HWaddress           Flags Mask            Iface\n"+
                 "whyme.xxxxxxxxxxxxxxx.c  ether   00:11:D8:DF:2B:9C   C                     eth2";
        result = "batoo.yyyyyy.org (70.55.60.9) -- no entry";
        //windows
        result = " 56 3:  10.1.12.203           00-11-d8-df-2b-9c     dynamic";
        result = " 2 12:Interface: 10.1.12.134 --- 0x50003";
        */
         
        if ( System.getProperty("os.name").toLowerCase().startsWith("windows") )
        {
            return callArpToolWindows( ipAddress );
        }
 
        return callArpToolDefault( ipAddress );
    }
    
    private String callArpToolWindows ( String ipAddress ) throws IOException, InterruptedException
    {
        String[] cmdArray = null;
        
        cmdArray = new String[] { "ping", ipAddress };
        
        Runtime.getRuntime().exec( cmdArray ).waitFor();
        
        cmdArray = new String[] { "arp", "-a" };
        
        String stdOut = runConsoleCommand ("arp -a");
        
        String[] parts = stdOut.split("\n");
        for (int i = 0; i < parts.length; i++)
        {
        	String part = parts[i];
            if ( part.indexOf(ipAddress) > -1 )
            {
                return part;
            }
        }
        return null;
     }
    
    private String callArpToolDefault ( String ipAddress ) throws IOException, InterruptedException
    {
        String[] cmdArray = null;
        
        cmdArray = new String[] { "ping", ipAddress };
        
        Runtime.getRuntime().exec( cmdArray ).waitFor();
        
        cmdArray = new String[] { "arp", ipAddress };
        
        Process proc = Runtime.getRuntime().exec( cmdArray );
        proc.waitFor();

        String output = FileUtils.readInputStream(proc.getInputStream());

        return output.toString();
     }

	protected static boolean isMacAddress(String macAddressCandidate)
	{
		Pattern macPattern = Pattern.compile("[0-9a-fA-F]{2}-[0-9a-fA-F]{2}-[0-9a-fA-F]{2}-[0-9a-fA-F]{2}-[0-9a-fA-F]{2}-[0-9a-fA-F]{2}");
		Matcher m = macPattern.matcher(macAddressCandidate);
		return m.matches();
	}
}
